package yolo

import (
	"appengine"
	"appengine/datastore"
	"fmt"
	"strings"
	"time"
)

const (
	issueMaxDescriptionLength = 200
)

func init() {
	RegisterCacheableType(Issue{})
}

func CreateIssueId(siteURL, issueId interface{}) string {
	return fmt.Sprintf("%s/%v", siteURL, issueId)
}

func ParseIssueId(c appengine.Context, id string) (string, string) {
	s := strings.Split(id, "/")
	if len(s) != 2 {
		c.Errorf("%s: Invalid issue Id: %v", currentLocation(), id)
		return "", ""
	}
	return s[0], s[1]
}

type Issue struct {
	IssueId string  // site-local issue Id, e.g. "12345"
	OwnerEmail string
	Subject string
	Description string
	Created time.Time
	Modified time.Time
	Closed bool
	BaseURL string
	SiteURL string  // e.g. codereview.chromium.org
	Bugs []int64
	Commit int64
}

func (issue* Issue) Key(c appengine.Context) *datastore.Key {
	return datastore.NewKey(c, "Issue", issue.Id(), 0, nil)
}

func (issue* Issue) Id() string {
	return CreateIssueId(issue.SiteURL, issue.IssueId)
}

func (issue* Issue) stringKey(c appengine.Context) string {
	return issue.Key(c).Encode()
}

func (issue* Issue) Store(c appengine.Context) error {
	// Make sure we also have the owner in the datastore.
	AddUser(c, issue.OwnerEmail)
	return StoreCachedObject(c, issue.stringKey(c), issue, true)
}

func (issue* Issue) Delete(c appengine.Context) error {
	return DeleteCachedObject(c, issue.stringKey(c))
}

func GetIssueById(c appengine.Context, id string, issue *Issue) error {
	key := datastore.NewKey(c, "Issue", id, 0, nil)
	err := GetCachedObject(c, key.Encode(), issue)
	return err
}

func GetIssues(c appengine.Context, email string, offset, limit int, issues interface{}) error {
	q := datastore.NewQuery("Issue").Order("-Modified").Offset(offset).Limit(limit)
	if email != "" {
		q = q.Filter("OwnerEmail =", email)
	}
	_, err := q.GetAll(c, issues)
	return err
}

// IssueDetail and IssueHandler ----------------------------------------------
type IssueDetail struct {
	IssueURL string
	FullDescription string
	Cc []string
	Reviewers []string
	Files []string
}

type IssueHandler interface {
	LoadDetail(c appengine.Context, issueId string, detail* IssueDetail) error
}

var handlers = make(map[string]func(string) IssueHandler)

func RegisterIssueHandler(site string, f func(string) IssueHandler) {
	handlers[site] = f
}

func GetIssueHandler(c appengine.Context, siteURL string) IssueHandler {
	f, _ := handlers[siteURL]
	if f != nil {
		return f(siteURL)
	}
	return nil
}

func GetIssueHandlerById(c appengine.Context, id string) IssueHandler {
	siteURL, _ := ParseIssueId(c, id)
	return GetIssueHandler(c, siteURL)
}

type IssueDetailData struct {
	Issue Issue
	IssueDetail IssueDetail
}

func GetIssueDetailDataById(c appengine.Context, id string) (*IssueDetailData, error) {
	issue := new(IssueDetailData)

	ch := make(chan error)

	go func() {
		ch <- GetIssueById(c, id, &issue.Issue)
	}()

	go func() {
		site, issueId := ParseIssueId(c, id)
		handler := GetIssueHandler(c, site)
		if handler == nil {
			ch <- NewYoloError(fmt.Sprintf("No handler found for the issue: %s (%s)", site, id))
			return
		}
		ch <- handler.LoadDetail(c, issueId, &issue.IssueDetail)
	}()

	for i := 0; i < 2; i++ {
		err := <-ch
		if err != nil {
			return nil, err
		}
	}
	return issue, nil
}
